package top.java.multithreading;

import java.util.concurrent.*;

public class ExecutorServiceTest1 {

    /**
     *    线程池就是一个可以复用线程的技术。
     *
     *    不使用线程池的问题：
     *     如果用户每发起一个请求，后台就创建一个新线程来处理，下次新任务来了又要创建新线程，
     *     而创建新线程的开销是很大的，这样会严重影响系统的性能。
     *
     *
     *     JDK 5.0起提供了代表线程池的接口：ExecutorService
     *     如何得到线程池对象：
     *      方式一：使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
     *      方式二：使用Executors（线程池的工具类）调用方法返回不同特点的线程池对象
     *
     *      参数一：指定线程池的线程数量（核心线程）： corePoolSize              不能小于0
     *      参数二：指定线程池可支持的最大线程数： maximumPoolSize              最大数量 >= 核心线程数量
     *      参数三：指定临时线程的最大存活时间： keepAliveTime                 不能小于0
     *      参数四：指定存活时间的单位(秒、分、时、天)： unit                    时间单位
     *      参数五：指定任务队列： workQueue                                不能为null
     *      参数六：指定用哪个线程工厂创建线程： threadFactory                 不能为null
     *      参数七：指定线程忙，任务满的时候，新任务来了怎么办： handler           不能为null
     *
     *      核心线程   最大线程数   临时线程空闲时间 空闲时间的单位 任务队列   线程工厂  任务拒绝策略
     *
     *      临时线程什么时候创建啊？
     *          新任务提交时发现核心线程都在忙，任务队列也满了，并且还可以创建临时线程，此时才会创建临时线程。
     *
     *      什么时候会开始拒绝任务？
     *          核心线程和临时线程都在忙，任务队列也满了，新的任务过来的时候才会开始任务拒绝。
     */

    //ThreadPoolExecutor构造器的参数说明
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    /**
     线程池参数：
       ThreadPoolExecutor(int corePoolSize,
       int maximumPoolSize,
       long keepAliveTime,
       TimeUnit unit,
       BlockingQueue<Runnable> workQueue,
       ThreadFactory threadFactory,
       RejectedExecutionHandler handler)

     ExecutorService的常用方法：
       void execute(Runnable command)   执行任务/命令，没有返回值，一般用来执行 Runnable 任务
       Future<T> submit(Callable<T> task) 执行任务，返回未来任务对象获取线程结果，一般拿来执行Callable任务

     任务拒绝策略：
       ThreadPoolExecutor.AbortPolicy         丢弃任务并抛出RejectedExecutionException异常。默认的策略
       ThreadPoolExecutor.DiscardPolicy       丢弃任务，但是不抛出异常 这是不推荐的做法
       ThreadPoolExecutor.DiscardOldestPolicy 抛弃队列中等待最久的任务 然后把当前任务加入队列中
       ThreadPoolExecutor.CallerRunsPolicy    由主线程负责调用任务的run()方法从而绕过线程池直接执行

     线程池如何处理Runnable任务
       使用ExecutorService的方法：
       void  execute(Runnable target)

     线程池如何处理Callable任务，并得到任务执行完后返回的结果。
       使用ExecutorService的方法：
       Future<T> submit(Callable<T> command)

     */

        //创建线程池
        ExecutorService pool = new ThreadPoolExecutor(3, 5,
                6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
System.out.println(
        "------------------------处理Runnable任务----------------------------"
);
/*
        //给任务让线程处理
        Runnable runnable = new MyRunnable();
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);

        //上面三个为核心线程，再创建线程则进入队列

        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);

        //上面五个为进入任务队列等待的线程，再创建线程则线程池创建临时线程

        pool.execute(runnable);     //临时线程1
        pool.execute(runnable);     //临时线程2

        //已满足最大线程数5和任务队列5，再创建触发任务拒绝策略
        pool.execute(runnable);
*/
System.out.println(
 "------------------------处理Callable任务----------------------------"
);
        Future f1 = pool.submit(new MyCallable(100));
        Future f2 = pool.submit(new MyCallable(200));
        Future f3 = pool.submit(new MyCallable(300));
        Future f4 = pool.submit(new MyCallable(400));
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

/**
 *      自定义线程池对象
 */
class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "：" + i);
        }
        try {
            System.out.println(Thread.currentThread().getName() + "线程休眠");
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable {

    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i;
        }
        System.out.println(Thread.currentThread().getName() + "：1-" + n + "的和为：" + sum);
        return sum;
    }
}